ગુજરાતી

જટિલ સ્ટેટ મેનેજમેન્ટ માટે રિએક્ટ કન્ટેક્સ્ટ API ની એડવાન્સ્ડ પેટર્ન્સ, કમ્પાઉન્ડ કમ્પોનન્ટ્સ અને ઓપ્ટિમાઇઝ્ડ પરફોર્મન્સ તકનીકોનું અન્વેષણ કરો.

સ્ટેટ મેનેજમેન્ટ માટે એડવાન્સ્ડ રિએક્ટ કન્ટેક્સ્ટ API પેટર્ન્સ

રિએક્ટ કન્ટેક્સ્ટ API તમારી એપ્લિકેશનમાં પ્રોપ ડ્રિલિંગ વિના સ્ટેટ શેર કરવા માટે એક શક્તિશાળી મિકેનિઝમ પ્રદાન કરે છે. જ્યારે તેનો મૂળભૂત ઉપયોગ સીધો છે, ત્યારે તેની સંપૂર્ણ ક્ષમતાનો લાભ લેવા માટે એડવાન્સ્ડ પેટર્ન્સને સમજવાની જરૂર છે જે જટિલ સ્ટેટ મેનેજમેન્ટના દૃશ્યોને સંભાળી શકે છે. આ લેખ આમાંની ઘણી પેટર્ન્સનું અન્વેષણ કરે છે, જે તમારા રિએક્ટ ડેવલપમેન્ટને ઉચ્ચ સ્તરે લઈ જવા માટે વ્યવહારુ ઉદાહરણો અને કાર્યક્ષમ આંતરદૃષ્ટિ પ્રદાન કરે છે.

બેઝિક કન્ટેક્સ્ટ API ની મર્યાદાઓને સમજવી

એડવાન્સ્ડ પેટર્ન્સમાં ઊંડા ઉતરતા પહેલાં, બેઝિક કન્ટેક્સ્ટ API ની મર્યાદાઓને સ્વીકારવી મહત્વપૂર્ણ છે. સરળ, વૈશ્વિક રીતે સુલભ સ્ટેટ માટે યોગ્ય હોવા છતાં, તે વારંવાર બદલાતા સ્ટેટવાળી જટિલ એપ્લિકેશનો માટે બોજારૂપ અને બિનકાર્યક્ષમ બની શકે છે. કન્ટેક્સ્ટનો ઉપયોગ કરતો દરેક કમ્પોનન્ટ જ્યારે પણ કન્ટેક્સ્ટ વેલ્યુ બદલાય ત્યારે ફરીથી રેન્ડર થાય છે, ભલે તે કમ્પોનન્ટ સ્ટેટના તે ચોક્કસ ભાગ પર આધાર રાખતો ન હોય જે અપડેટ થયો હોય. આનાથી પરફોર્મન્સમાં અવરોધો આવી શકે છે.

પેટર્ન 1: કન્ટેક્સ્ટ સાથે કમ્પાઉન્ડ કમ્પોનન્ટ્સ

કમ્પાઉન્ડ કમ્પોનન્ટ પેટર્ન કન્ટેક્સ્ટ API ને સંબંધિત કમ્પોનન્ટ્સનો એક સ્યુટ બનાવીને વધારે છે જે કન્ટેક્સ્ટ દ્વારા સ્પષ્ટપણે સ્ટેટ અને લોજિક શેર કરે છે. આ પેટર્ન પુનઃઉપયોગીતાને પ્રોત્સાહન આપે છે અને ગ્રાહકો માટે API ને સરળ બનાવે છે. આનાથી જટિલ લોજિકને સરળ અમલીકરણ સાથે સમાવી શકાય છે.

ઉદાહરણ: એક ટેબ કમ્પોનન્ટ

ચાલો આપણે આને ટેબ કમ્પોનન્ટ સાથે સમજીએ. બહુવિધ સ્તરો દ્વારા પ્રોપ્સ પસાર કરવાને બદલે, Tab કમ્પોનન્ટ્સ એક શેર્ડ કન્ટેક્સ્ટ દ્વારા સ્પષ્ટપણે સંચાર કરે છે.

// TabContext.js
import React, { createContext, useContext, useState, ReactNode } from 'react';

interface TabContextType {
  activeTab: string;
  setActiveTab: (tab: string) => void;
}

const TabContext = createContext(undefined);

interface TabProviderProps {
  children: ReactNode;
  defaultTab: string;
}

export const TabProvider: React.FC = ({ children, defaultTab }) => {
  const [activeTab, setActiveTab] = useState(defaultTab);

  const value: TabContextType = {
    activeTab,
    setActiveTab,
  };

  return {children};
};

export const useTabContext = () => {
  const context = useContext(TabContext);
  if (!context) {
    throw new Error('useTabContext must be used within a TabProvider');
  }
  return context;
};

// TabList.js
import React, { ReactNode } from 'react';

interface TabListProps {
  children: ReactNode;
}

export const TabList: React.FC = ({ children }) => {
  return 
{children}
; }; // Tab.js import React, { ReactNode } from 'react'; import { useTabContext } from './TabContext'; interface TabProps { label: string; children: ReactNode; } export const Tab: React.FC = ({ label, children }) => { const { activeTab, setActiveTab } = useTabContext(); const isActive = activeTab === label; const handleClick = () => { setActiveTab(label); }; return ( ); }; // TabPanel.js import React, { ReactNode } from 'react'; import { useTabContext } from './TabContext'; interface TabPanelProps { label: string; children: ReactNode; } export const TabPanel: React.FC = ({ label, children }) => { const { activeTab } = useTabContext(); const isActive = activeTab === label; return ( ); };
// Usage
import { TabProvider, TabList, Tab, TabPanel } from './components/Tabs';

function App() {
  return (
    
      
        Tab 1
        Tab 2
        Tab 3
      
      Content for Tab 1
      Content for Tab 2
      Content for Tab 3
    
  );
}

export default App;

લાભો:

પેટર્ન 2: ડાયનેમિક કન્ટેક્સ્ટ્સ

કેટલાક દૃશ્યોમાં, તમારે કમ્પોનન્ટ ટ્રીમાં કમ્પોનન્ટની સ્થિતિ અથવા અન્ય ડાયનેમિક પરિબળોના આધારે અલગ-અલગ કન્ટેક્સ્ટ વેલ્યુની જરૂર પડી શકે છે. ડાયનેમિક કન્ટેક્સ્ટ્સ તમને ચોક્કસ શરતોના આધારે બદલાતી કન્ટેક્સ્ટ વેલ્યુ બનાવવા અને પ્રદાન કરવાની મંજૂરી આપે છે.

ઉદાહરણ: ડાયનેમિક કન્ટેક્સ્ટ્સ સાથે થીમિંગ

એક થીમિંગ સિસ્ટમનો વિચાર કરો જ્યાં તમે વપરાશકર્તાની પસંદગીઓ અથવા એપ્લિકેશનના વિભાગના આધારે અલગ-અલગ થીમ પ્રદાન કરવા માંગો છો. આપણે લાઇટ અને ડાર્ક થીમ સાથે એક સરળ ઉદાહરણ બનાવી શકીએ છીએ.

// ThemeContext.js
import React, { createContext, useContext, useState, ReactNode } from 'react';

interface Theme {
  background: string;
  color: string;
}

interface ThemeContextType {
  theme: Theme;
  toggleTheme: () => void;
}

const defaultTheme: Theme = {
    background: 'white',
    color: 'black'
};

const darkTheme: Theme = {
    background: 'black',
    color: 'white'
};

const ThemeContext = createContext({
    theme: defaultTheme,
    toggleTheme: () => {}
});

interface ThemeProviderProps {
  children: ReactNode;
}

export const ThemeProvider: React.FC = ({ children }) => {
  const [isDarkTheme, setIsDarkTheme] = useState(false);
  const theme = isDarkTheme ? darkTheme : defaultTheme;

  const toggleTheme = () => {
    setIsDarkTheme(!isDarkTheme);
  };

  const value: ThemeContextType = {
    theme,
    toggleTheme,
  };

  return {children};
};

export const useTheme = () => {
  return useContext(ThemeContext);
};
// Usage
import { useTheme, ThemeProvider } from './ThemeContext';

function MyComponent() {
  const { theme, toggleTheme } = useTheme();

  return (
    

This is a themed component.

); } function App() { return ( ); } export default App;

આ ઉદાહરણમાં, ThemeProvider isDarkTheme સ્ટેટના આધારે ડાયનેમિક રીતે થીમ નક્કી કરે છે. useTheme હૂકનો ઉપયોગ કરતા કમ્પોનન્ટ્સ જ્યારે થીમ બદલાશે ત્યારે આપમેળે ફરીથી રેન્ડર થશે.

પેટર્ન 3: જટિલ સ્ટેટ માટે useReducer સાથે કન્ટેક્સ્ટ

જટિલ સ્ટેટ લોજિકના સંચાલન માટે, કન્ટેક્સ્ટ API ને useReducer સાથે જોડવું એ એક ઉત્તમ અભિગમ છે. useReducer ક્રિયાઓના આધારે સ્ટેટને અપડેટ કરવાની એક સંરચિત રીત પ્રદાન કરે છે, અને કન્ટેક્સ્ટ API તમને આ સ્ટેટ અને ડિસ્પેચ ફંક્શનને તમારી એપ્લિકેશનમાં શેર કરવાની મંજૂરી આપે છે.

ઉદાહરણ: એક સરળ ટુડુ લિસ્ટ

// TodoContext.js
import React, { createContext, useContext, useReducer, ReactNode } from 'react';

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

interface TodoState {
  todos: Todo[];
}

type TodoAction = 
  | { type: 'ADD_TODO'; text: string } 
  | { type: 'TOGGLE_TODO'; id: number } 
  | { type: 'DELETE_TODO'; id: number };

interface TodoContextType {
  state: TodoState;
  dispatch: React.Dispatch;
}

const initialState: TodoState = {
  todos: [],
};

const todoReducer = (state: TodoState, action: TodoAction): TodoState => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, { id: Date.now(), text: action.text, completed: false }],
      };
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map((todo) =>
          todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
        ),
      };
    case 'DELETE_TODO':
      return {
        ...state,
        todos: state.todos.filter((todo) => todo.id !== action.id),
      };
    default:
      return state;
  }
};

const TodoContext = createContext(undefined);

interface TodoProviderProps {
  children: ReactNode;
}

export const TodoProvider: React.FC = ({ children }) => {
  const [state, dispatch] = useReducer(todoReducer, initialState);

  const value: TodoContextType = {
    state,
    dispatch,
  };

  return {children};
};

export const useTodo = () => {
  const context = useContext(TodoContext);
  if (!context) {
    throw new Error('useTodo must be used within a TodoProvider');
  }
  return context;
};
// Usage
import { useTodo, TodoProvider } from './TodoContext';

function TodoList() {
  const { state, dispatch } = useTodo();

  return (
    
    {state.todos.map((todo) => (
  • {todo.text}
  • ))}
); } function AddTodo() { const { dispatch } = useTodo(); const [text, setText] = React.useState(''); const handleSubmit = (e) => { e.preventDefault(); dispatch({ type: 'ADD_TODO', text }); setText(''); }; return (
setText(e.target.value)} />
); } function App() { return ( ); } export default App;

આ પેટર્ન સ્ટેટ મેનેજમેન્ટ લોજિકને રિડ્યુસરની અંદર કેન્દ્રિત કરે છે, જે તેને તર્કબદ્ધ અને પરીક્ષણ કરવાનું સરળ બનાવે છે. કમ્પોનન્ટ્સ સ્ટેટને સીધું સંચાલિત કર્યા વિના સ્ટેટને અપડેટ કરવા માટે ક્રિયાઓ ડિસ્પેચ કરી શકે છે.

પેટર્ન 4: `useMemo` અને `useCallback` સાથે ઓપ્ટિમાઇઝ્ડ કન્ટેક્સ્ટ અપડેટ્સ

જેમ કે પહેલા ઉલ્લેખ કર્યો છે, કન્ટેક્સ્ટ API સાથે એક મુખ્ય પરફોર્મન્સ વિચારણા બિનજરૂરી રી-રેન્ડર્સ છે. useMemo અને useCallback નો ઉપયોગ આ રી-રેન્ડર્સને અટકાવી શકે છે, તે સુનિશ્ચિત કરીને કે કન્ટેક્સ્ટ વેલ્યુના ફક્ત જરૂરી ભાગો જ અપડેટ થાય છે, અને ફંક્શન રેફરન્સ સ્થિર રહે છે.

ઉદાહરણ: થીમ કન્ટેક્સ્ટને ઓપ્ટિમાઇઝ કરવું

// OptimizedThemeContext.js
import React, { createContext, useContext, useState, useMemo, useCallback, ReactNode } from 'react';

interface Theme {
  background: string;
  color: string;
}

interface ThemeContextType {
  theme: Theme;
  toggleTheme: () => void;
}

const defaultTheme: Theme = {
    background: 'white',
    color: 'black'
};

const darkTheme: Theme = {
    background: 'black',
    color: 'white'
};

const ThemeContext = createContext({
    theme: defaultTheme,
    toggleTheme: () => {}
});

interface ThemeProviderProps {
  children: ReactNode;
}

export const ThemeProvider: React.FC = ({ children }) => {
  const [isDarkTheme, setIsDarkTheme] = useState(false);
  const theme = isDarkTheme ? darkTheme : defaultTheme;

  const toggleTheme = useCallback(() => {
    setIsDarkTheme(!isDarkTheme);
  }, [isDarkTheme]);

  const value: ThemeContextType = useMemo(() => ({
    theme,
    toggleTheme,
  }), [theme, toggleTheme]);

  return {children};
};

export const useTheme = () => {
  return useContext(ThemeContext);
};

સમજૂતી:

useCallback વિના, toggleTheme ફંક્શન ThemeProvider ના દરેક રેન્ડર પર ફરીથી બનાવવામાં આવશે, જેના કારણે value બદલાશે અને કોઈપણ ઉપભોક્તા કમ્પોનન્ટમાં રી-રેન્ડર્સ ટ્રિગર થશે, ભલે થીમ પોતે બદલાઈ ન હોય. useMemo સુનિશ્ચિત કરે છે કે નવી value ફક્ત ત્યારે જ બને છે જ્યારે તેની નિર્ભરતા (theme અથવા toggleTheme) બદલાય.

પેટર્ન 5: કન્ટેક્સ્ટ સિલેક્ટર્સ

કન્ટેક્સ્ટ સિલેક્ટર્સ કમ્પોનન્ટ્સને કન્ટેક્સ્ટ વેલ્યુના ફક્ત ચોક્કસ ભાગો પર સબ્સ્ક્રાઇબ કરવાની મંજૂરી આપે છે. આનાથી બિનજરૂરી રી-રેન્ડર્સ અટકે છે જ્યારે કન્ટેક્સ્ટના અન્ય ભાગો બદલાય છે. આને પ્રાપ્ત કરવા માટે `use-context-selector` જેવી લાઇબ્રેરીઓ અથવા કસ્ટમ અમલીકરણનો ઉપયોગ કરી શકાય છે.

કસ્ટમ કન્ટેક્સ્ટ સિલેક્ટરનો ઉપયોગ કરીને ઉદાહરણ

// useCustomContextSelector.js
import { useContext, useState, useRef, useEffect } from 'react';

function useCustomContextSelector(
  context: React.Context,
  selector: (value: T) => S
): S {
  const value = useContext(context);
  const [selected, setSelected] = useState(() => selector(value));
  const latestSelector = useRef(selector);
  latestSelector.current = selector;

  useEffect(() => {
    let didUnmount = false;
    let lastSelected = selected;

    const subscription = () => {
      if (didUnmount) {
        return;
      }
      const nextSelected = latestSelector.current(value);
      if (!Object.is(lastSelected, nextSelected)) {
        lastSelected = nextSelected;
        setSelected(nextSelected);
      }
    };

    // You would typically subscribe to context changes here. Since this is a simplified
    // example, we'll just call subscription immediately to initialize.
    subscription();

    return () => {
      didUnmount = true;
      // Unsubscribe from context changes here, if applicable.
    };
  }, [value]); // Re-run effect whenever the context value changes

  return selected;
}

export default useCustomContextSelector;
// ThemeContext.js (Simplified for brevity)
import React, { createContext, useState, ReactNode } from 'react';

interface Theme {
  background: string;
  color: string;
}

interface ThemeContextType {
  theme: Theme;
  setTheme: (newTheme: Theme) => void; 
}

const ThemeContext = createContext(undefined);

interface ThemeProviderProps {
  children: ReactNode;
  initialTheme: Theme;
}

export const ThemeProvider: React.FC = ({ children, initialTheme }) => {
  const [theme, setTheme] = useState(initialTheme);

  const value: ThemeContextType = {
    theme,
    setTheme
  };

  return {children};
};

export const useThemeContext = () => {
    const context = React.useContext(ThemeContext);
    if (!context) {
        throw new Error("useThemeContext must be used within a ThemeProvider");
    }
    return context;
};

export default ThemeContext;
// Usage
import useCustomContextSelector from './useCustomContextSelector';
import ThemeContext, { ThemeProvider, useThemeContext } from './ThemeContext';

function BackgroundComponent() {
  const background = useCustomContextSelector(ThemeContext, (context) => context.theme.background);
  return 
Background
; } function ColorComponent() { const color = useCustomContextSelector(ThemeContext, (context) => context.theme.color); return
Color
; } function App() { const { theme, setTheme } = useThemeContext(); const toggleTheme = () => { setTheme({ background: theme.background === 'white' ? 'black' : 'white', color: theme.color === 'black' ? 'white' : 'black' }); }; return ( ); } export default App;

આ ઉદાહરણમાં, BackgroundComponent ફક્ત ત્યારે જ રી-રેન્ડર થાય છે જ્યારે થીમની background પ્રોપર્ટી બદલાય છે, અને ColorComponent ફક્ત ત્યારે જ રી-રેન્ડર થાય છે જ્યારે color પ્રોપર્ટી બદલાય છે. આનાથી જ્યારે સમગ્ર કન્ટેક્સ્ટ વેલ્યુ બદલાય ત્યારે બિનજરૂરી રી-રેન્ડર્સ ટાળી શકાય છે.

પેટર્ન 6: ક્રિયાઓને સ્ટેટથી અલગ કરવી

મોટી એપ્લિકેશનો માટે, કન્ટેક્સ્ટ વેલ્યુને બે અલગ-અલગ કન્ટેક્સ્ટમાં વિભાજીત કરવાનો વિચાર કરો: એક સ્ટેટ માટે અને બીજું ક્રિયાઓ (ડિસ્પેચ ફંક્શન્સ) માટે. આનાથી કોડની ગોઠવણ અને પરીક્ષણક્ષમતામાં સુધારો થઈ શકે છે.

અલગ સ્ટેટ અને એક્શન કન્ટેક્સ્ટ્સ સાથે ટુડુ લિસ્ટનું ઉદાહરણ

// TodoStateContext.js
import React, { createContext, useContext, useReducer, ReactNode } from 'react';

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

interface TodoState {
  todos: Todo[];
}

const initialState: TodoState = {
  todos: [],
};

const TodoStateContext = createContext(initialState);

interface TodoStateProviderProps {
  children: ReactNode;
}

export const TodoStateProvider: React.FC = ({ children }) => {
  const [state] = useReducer(todoReducer, initialState);

  return {children};
};

export const useTodoState = () => {
  return useContext(TodoStateContext);
};

// TodoActionContext.js
import React, { createContext, useContext, Dispatch, ReactNode } from 'react';

type TodoAction = 
  | { type: 'ADD_TODO'; text: string } 
  | { type: 'TOGGLE_TODO'; id: number } 
  | { type: 'DELETE_TODO'; id: number };

const TodoActionContext = createContext | undefined>(undefined);

interface TodoActionProviderProps {
    children: ReactNode;
}

export const TodoActionProvider: React.FC = ({children}) => {
    const [, dispatch] = useReducer(todoReducer, initialState);

    return {children};
};


export const useTodoDispatch = () => {
  const dispatch = useContext(TodoActionContext);
  if (!dispatch) {
    throw new Error('useTodoDispatch must be used within a TodoActionProvider');
  }
  return dispatch;
};

// todoReducer.js
export const todoReducer = (state: TodoState, action: TodoAction): TodoState => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, { id: Date.now(), text: action.text, completed: false }],
      };
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map((todo) =>
          todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
        ),
      };
    case 'DELETE_TODO':
      return {
        ...state,
        todos: state.todos.filter((todo) => todo.id !== action.id),
      };
    default:
      return state;
  }
};
// Usage
import { useTodoState, TodoStateProvider } from './TodoStateContext';
import { useTodoDispatch, TodoActionProvider } from './TodoActionContext';

function TodoList() {
  const state = useTodoState();

  return (
    
    {state.todos.map((todo) => (
  • {todo.text}
  • ))}
); } function TodoActions({ todo }) { const dispatch = useTodoDispatch(); return ( <> ); } function AddTodo() { const dispatch = useTodoDispatch(); const [text, setText] = React.useState(''); const handleSubmit = (e) => { e.preventDefault(); dispatch({ type: 'ADD_TODO', text }); setText(''); }; return (
setText(e.target.value)} />
); } function App() { return ( ); } export default App;

આ વિભાજન કમ્પોનન્ટ્સને ફક્ત તે જ કન્ટેક્સ્ટ પર સબ્સ્ક્રાઇબ કરવાની મંજૂરી આપે છે જેની તેમને જરૂર હોય, બિનજરૂરી રી-રેન્ડર્સ ઘટાડે છે. આનાથી રિડ્યુસર અને દરેક કમ્પોનન્ટને અલગથી યુનિટ ટેસ્ટ કરવાનું પણ સરળ બને છે. ઉપરાંત, પ્રોવાઇડર રેપિંગનો ક્રમ મહત્વપૂર્ણ છે. ActionProviderStateProvider ને રેપ કરવું પડશે.

શ્રેષ્ઠ પ્રથાઓ અને વિચારણાઓ

નિષ્કર્ષ

રિએક્ટ કન્ટેક્સ્ટ API એ સ્ટેટ મેનેજમેન્ટ માટે એક બહુમુખી સાધન છે. આ એડવાન્સ્ડ પેટર્ન્સને સમજીને અને લાગુ કરીને, તમે જટિલ સ્ટેટને અસરકારક રીતે સંચાલિત કરી શકો છો, પરફોર્મન્સને ઓપ્ટિમાઇઝ કરી શકો છો, અને વધુ જાળવણી યોગ્ય અને માપી શકાય તેવી રિએક્ટ એપ્લિકેશનો બનાવી શકો છો. તમારી ચોક્કસ જરૂરિયાતો માટે યોગ્ય પેટર્ન પસંદ કરવાનું અને તમારા કન્ટેક્સ્ટના ઉપયોગના પરફોર્મન્સ પરના પ્રભાવોને કાળજીપૂર્વક ધ્યાનમાં લેવાનું યાદ રાખો.

જેમ જેમ રિએક્ટ વિકસિત થાય છે, તેમ તેમ કન્ટેક્સ્ટ API ની આસપાસની શ્રેષ્ઠ પ્રથાઓ પણ વિકસિત થશે. નવી તકનીકો અને લાઇબ્રેરીઓ વિશે માહિતગાર રહેવાથી તમે આધુનિક વેબ ડેવલપમેન્ટના સ્ટેટ મેનેજમેન્ટના પડકારોને પહોંચી વળવા માટે સજ્જ છો તેની ખાતરી થશે. વધુ સૂક્ષ્મ-પ્રતિક્રિયાશીલતા માટે સિગ્નલ સાથે કન્ટેક્સ્ટનો ઉપયોગ કરવા જેવી ઉભરતી પેટર્ન્સનું અન્વેષણ કરવાનું વિચારો.